package cn.desk.commons.dao.impl;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import cn.desk.commons.dao.IBaseDao;
import cn.desk.commons.entity.BaseEntity;
import cn.desk.commons.entity.LogicallyDelete;
import cn.desk.commons.entity.PageMode;
import cn.desk.commons.util.QueryCondition;

public class BaseDaoImpl<T extends BaseEntity> extends HibernateDaoSupport
		implements IBaseDao<T> {
	private static final Logger log = LoggerFactory
			.getLogger(BaseDaoImpl.class);
	
	@Override
	public void insert(T t) {
		log.debug("insert start");
		if(t instanceof LogicallyDelete){
			((LogicallyDelete) t).setDeleteClass(LogicallyDelete.DELETECLASS_NORMAL);
		}
		getHibernateTemplate().save(t);
		log.debug("insert end");
	}
	
	@Override
	public void modify(T t) {
		log.debug("modify start");
		getHibernateTemplate().update(t);
		log.debug("modify end");
	}
	@Override
	public void merge(T t) {
		log.debug("merge start");
		getHibernateTemplate().merge(t);
		log.debug("merge end");
	}
	
	@Override
	public void delete(T t) {
		log.debug("delete start");
		getHibernateTemplate().delete(t);
		log.debug("delete end");
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<T> list(final T t) {
		log.debug("list start");
		try {
			return getHibernateTemplate().executeFind(
					new HibernateCallback<List<T>>() {

						@Override
						public List<T> doInHibernate(Session session)
								throws HibernateException, SQLException {
							Criteria criteria = session.createCriteria(t
									.getClass());
							if (t.getId() != null) {
								criteria.add(Restrictions.like("id",
										"%" + t.getId() + "%"));
							}
							criteria.add(Example.create(t).enableLike(MatchMode.ANYWHERE));
							criteria.addOrder(Order.asc("id"));
							criteria.setResultTransformer( 
								     CriteriaSpecification.DISTINCT_ROOT_ENTITY);
							return criteria.list();
						}
					});
		} finally {
			log.debug("list end");
		}
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public T load(Class<?> clazz, Serializable pk) {
		log.debug("load start");
		try{
			T t=(T) getHibernateTemplate().get(clazz, pk);
			if(t instanceof LogicallyDelete){
				LogicallyDelete ld=(LogicallyDelete) t;
				if(LogicallyDelete.DELETECLASS_DELETED.equals(ld.getDeleteClass())){
					return null;
				}
			}
			return t;
		}finally{
			log.debug("load end");
		}
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public PageMode<T> listPage(final PageMode<T> page, final T t) {
		//
		log.debug("listPage start");
		try {
			List<T> list = getHibernateTemplate().executeFind(
					new HibernateCallback<List<T>>() {

						@Override
						public List<T> doInHibernate(Session session)
								throws HibernateException, SQLException {
							Criteria criteria = session.createCriteria(t
									.getClass());
							if (t.getId() != null) {
								criteria.add(Restrictions.like("id",
										"%" + t.getId() + "%"));
							}
							criteria.setResultTransformer( 
								     CriteriaSpecification.DISTINCT_ROOT_ENTITY);
							criteria.add(Example.create(t).enableLike(MatchMode.ANYWHERE));
							int length = criteria.list().size();
							page.setTotalPage((length + page.getPageSize() - 1)
									/ page.getPageSize());
							criteria.setFirstResult((page.getCurrentPage() - 1)
									* page.getPageSize());
							criteria.setMaxResults(page.getPageSize());
							criteria.addOrder(Order.asc("id"));
							return criteria.list();
						}
					});
			page.setData(list);
			return page;
		} finally {
			log.debug("listPage end");
		}
	}
	
	@SuppressWarnings("unchecked")
	public List<T> listInByProperty(final Class<?> clazz,final String property, final String[] dependencies) {
		log.debug("listInByProperty start");
		try{
		return  getHibernateTemplate().executeFind(new HibernateCallback<List<T>>() {

			@Override
			public List<T> doInHibernate(Session session) throws HibernateException,
					SQLException {
				Criteria criteria= session.createCriteria(clazz);
				criteria.add(Restrictions.in(property, dependencies));
				criteria.setResultTransformer( 
					     CriteriaSpecification.DISTINCT_ROOT_ENTITY);
				return criteria.list();
			}
		});
		}finally{
			log.debug("listInByProperty end");
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> listByProperty(final Class<?> clazz, final String property, final Object value) {
		log.debug("listInByProperty start");
		try{
			return  getHibernateTemplate().executeFind(new HibernateCallback<List<T>>() {
	
				@Override
				public List<T> doInHibernate(Session session) throws HibernateException,
						SQLException {
					Criteria criteria= session.createCriteria(clazz);
					if(value==null){
						criteria.add(Restrictions.isNull(property));
					}else{
						criteria.add(Restrictions.eq(property, value));
					}
					criteria.setResultTransformer( 
						     CriteriaSpecification.DISTINCT_ROOT_ENTITY);
					return criteria.list();
				}
			});
		}finally{
			log.debug("listInByProperty end");
		}
	}

	@Override
	public String getMax(final Class<?> clazz, final String property) {
		log.debug("listInByProperty start");
		try{
			return  getHibernateTemplate().execute(new HibernateCallback<String>() {
	
				@Override
				public String doInHibernate(Session session) throws HibernateException,
						SQLException {
					Criteria criteria= session.createCriteria(clazz);
					criteria.setProjection( Projections.projectionList().add(Projections.max(property) ) );
					return (String) criteria.uniqueResult();
				}
			});
		}finally{
			log.debug("listInByProperty end");
		}
	}
	@SuppressWarnings("unchecked")
	@Override
	public PageMode<T> listPage(final PageMode<T> pageMode, final Class<?> clazz,
			final QueryCondition condition) {
		//
		log.debug("listPage start");
		try {
			List<T> list = getHibernateTemplate().executeFind(
					new HibernateCallback<List<T>>() {

						@Override
						public List<T> doInHibernate(Session session)
								throws HibernateException, SQLException {
							Criteria criteria = session.createCriteria(clazz);
							if(condition!=null){
								build( criteria, condition);
							}
							criteria.setResultTransformer( 
								     CriteriaSpecification.DISTINCT_ROOT_ENTITY);
							int length = criteria.list().size();
							pageMode.setTotalPage((length + pageMode.getPageSize() - 1)
									/ pageMode.getPageSize());
							criteria.setFirstResult((pageMode.getCurrentPage() - 1)
									* pageMode.getPageSize());
							criteria.setMaxResults(pageMode.getPageSize());
							return criteria.list();
						}
					});
			pageMode.setData(list);
			return pageMode;
		} finally {
			log.debug("listPage end");
		}
	}
	
	private void build(Criteria criteria,QueryCondition condition){
		Integer type=condition.getConditionType();
		if(type!=null){
			switch (type) {
			case QueryCondition.CONDITION_TYPE_EQ|QueryCondition.CONDITION_TYPE_AND:
				criteria.add(Restrictions.eq(condition.getProperty(), condition.getValue()));
				break;
			case QueryCondition.CONDITION_TYPE_LLIKE:
				criteria.add(Restrictions.like(condition.getProperty(), String.valueOf(condition.getValue()), MatchMode.START));
				break;
			case QueryCondition.CONDITION_TYPE_RLIKE:
				criteria.add(Restrictions.like(condition.getProperty(), String.valueOf(condition.getValue()), MatchMode.END));
				break;
			case QueryCondition.CONDITION_TYPE_RLIKE|QueryCondition.CONDITION_TYPE_LLIKE:
				criteria.add(Restrictions.like(condition.getProperty(), String.valueOf(condition.getValue()), MatchMode.ANYWHERE));
				break;
			case QueryCondition.CONDITION_TYPE_DESC:
				criteria.addOrder(Order.desc(condition.getProperty()));
				break;
			case QueryCondition.CONDITION_TYPE_ASC:
				criteria.addOrder(Order.asc(condition.getProperty()));
				break;
			case QueryCondition.CONDITION_TYPE_IS_NULL|QueryCondition.CONDITION_TYPE_AND:
				criteria.add(Restrictions.isNull(condition.getProperty()));
				break;
			default:
				break;
			}
			logger.debug("property:"+condition.getProperty());
		}
		List<QueryCondition> conditions=condition.getQueryConditions();
		if(conditions!=null){
			for(QueryCondition con:conditions){
				 build( criteria, con);
			}
		}
	}
	@SuppressWarnings("unchecked")
	@Override
	public List<T> list(final Class<?> clazz,final QueryCondition condition) {
		log.debug("list start");
		try {
			return getHibernateTemplate().executeFind(
					new HibernateCallback<List<T>>() {

						@Override
						public List<T> doInHibernate(Session session)
								throws HibernateException, SQLException {
							Criteria criteria = session.createCriteria(clazz);
							if(condition!=null){
								build( criteria, condition);
							}
							criteria.setResultTransformer( 
								     CriteriaSpecification.DISTINCT_ROOT_ENTITY);
							return criteria.list();
						}
					});
		} finally {
			log.debug("list end");
		}
	}

	@Override
	public T loadByCondition(final Class<?> clazz, final QueryCondition condition) {
		log.debug("list start");
		try {
			return getHibernateTemplate().execute(
					new HibernateCallback<T>() {

						@SuppressWarnings("unchecked")
						@Override
						public T doInHibernate(Session session)
								throws HibernateException, SQLException {
							Criteria criteria = session.createCriteria(clazz);
							if(condition!=null){
								build( criteria, condition);
							}
							return (T) criteria.uniqueResult();
						}
					});
		} finally {
			log.debug("list end");
		}
	}

	@Override
	public void exceHQL(String string, Map<String, Object> parameter) {
		log.debug("exceHQL start");
		Session session=getHibernateTemplate().getSessionFactory().openSession();
		Query q=session.createQuery(string);
		if(parameter!=null){
			for(String key:parameter.keySet()){
				q.setParameter(key, parameter.get(key));
			}
		}
		q.executeUpdate();
		log.debug("exceHQL end");
	}
}
